# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

include(CheckCXXSourceCompiles)
include(CheckCXXSymbolExists)
include(CheckIncludeFileCXX)
include(CheckFunctionExists)
include(CMakePushCheckState)

set(
  BOOST_LINK_STATIC "auto"
  CACHE STRING
  "Whether to link against boost statically or dynamically."
)
if("${BOOST_LINK_STATIC}" STREQUAL "auto")
  # Default to linking boost statically on Windows with MSVC
  if(MSVC)
    set(MELON_BOOST_LINK_STATIC ON)
  else()
    set(MELON_BOOST_LINK_STATIC OFF)
  endif()
else()
  set(MELON_BOOST_LINK_STATIC "${BOOST_LINK_STATIC}")
endif()
set(Boost_USE_STATIC_LIBS "${MELON_BOOST_LINK_STATIC}")

find_package(Boost
  COMPONENTS
    context
    filesystem
    program_options
    regex
    system
    thread
  REQUIRED
)
set(Boost_LIBRARIES Boost::boost Boost::context Boost::filesystem Boost::program_options Boost::regex Boost::system Boost::thread)
list(APPEND MELON_LINK_LIBRARIES ${Boost_LIBRARIES})

find_package(double-conversion CONFIG REQUIRED)
list(APPEND MELON_LINK_LIBRARIES double-conversion::double-conversion)

find_package(turbo CONFIG REQUIRED)
list(APPEND MELON_LINK_LIBRARIES turbo::turbo_static)

find_package(Libevent CONFIG REQUIRED)
list(APPEND MELON_LINK_LIBRARIES libevent::core libevent::extra)
if(NOT WIN32)
  list(APPEND MELON_LINK_LIBRARIES libevent::pthreads)
endif()


find_package(ZLIB MODULE REQUIRED)
set(MELON_HAVE_LIBZ ${ZLIB_FOUND})
if (ZLIB_FOUND)
  list(APPEND MELON_INCLUDE_DIRECTORIES ${ZLIB_INCLUDE_DIRS})
  list(APPEND MELON_LINK_LIBRARIES ${ZLIB_LIBRARIES})
  list(APPEND CMAKE_REQUIRED_LIBRARIES ${ZLIB_LIBRARIES})
endif()

find_package(OpenSSL 1.1.1 MODULE REQUIRED)
list(APPEND MELON_LINK_LIBRARIES ${OPENSSL_LIBRARIES})
list(APPEND MELON_INCLUDE_DIRECTORIES ${OPENSSL_INCLUDE_DIR})
list(APPEND CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES})
list(APPEND CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES})
list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
if (ZLIB_FOUND)
    list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES ${ZLIB_LIBRARIES})
endif()

find_package(BZip2 MODULE)
set(MELON_HAVE_LIBBZ2 ${BZIP2_FOUND})
if (BZIP2_FOUND)
  list(APPEND MELON_INCLUDE_DIRECTORIES ${BZIP2_INCLUDE_DIRS})
  list(APPEND MELON_LINK_LIBRARIES ${BZIP2_LIBRARIES})
endif()

find_package(LibLZMA MODULE)
set(MELON_HAVE_LIBLZMA ${LIBLZMA_FOUND})
if (LIBLZMA_FOUND)
  list(APPEND MELON_INCLUDE_DIRECTORIES ${LIBLZMA_INCLUDE_DIRS})
  list(APPEND MELON_LINK_LIBRARIES ${LIBLZMA_LIBRARIES})
endif()

if(NOT CMAKE_DISABLE_FIND_PACKAGE_LZ4)
  find_package(lz4 CONFIG)
  if(TARGET lz4::lz4)
    list(APPEND MELON_LINK_LIBRARIES lz4::lz4)
  endif()
endif()

if(NOT CMAKE_DISABLE_FIND_PACKAGE_Zstd)
  find_package(zstd CONFIG)
  if(TARGET zstd::libzstd_shared )
    set(MELON_HAVE_LIBZSTD 1)
    list(APPEND MELON_LINK_LIBRARIES zstd::libzstd_shared)
  elseif( TARGET zstd::libzstd_static )
    set(MELON_HAVE_LIBZSTD 1)
    list(APPEND MELON_LINK_LIBRARIES zstd::libzstd_static)
  endif()
endif()

if(NOT CMAKE_DISABLE_FIND_PACKAGE_Snappy)
  find_package(Snappy CONFIG)
  if(TARGET Snappy::snappy)
    list(APPEND MELON_LINK_LIBRARIES Snappy::snappy)
  endif()
endif()

find_package(LibDwarf)
list(APPEND MELON_LINK_LIBRARIES ${LIBDWARF_LIBRARIES})
list(APPEND MELON_INCLUDE_DIRECTORIES ${LIBDWARF_INCLUDE_DIRS})

find_package(Libiberty)
list(APPEND MELON_LINK_LIBRARIES ${LIBIBERTY_LIBRARIES})
list(APPEND MELON_INCLUDE_DIRECTORIES ${LIBIBERTY_INCLUDE_DIRS})

if(WITH_libaio)
  find_package(LibAIO)
  list(APPEND MELON_LINK_LIBRARIES ${LIBAIO_LIBRARIES})
  list(APPEND MELON_INCLUDE_DIRECTORIES ${LIBAIO_INCLUDE_DIRS})
endif()

if(WITH_liburing)
  find_package(LibUring)
  list(APPEND MELON_LINK_LIBRARIES ${LIBURING_LIBRARIES})
  list(APPEND MELON_INCLUDE_DIRECTORIES ${LIBURING_INCLUDE_DIRS})
endif()

if(NOT CMAKE_DISABLE_FIND_PACKAGE_unofficial-sodium)
  find_package(unofficial-sodium CONFIG)
  if(TARGET unofficial-sodium::sodium)
    list(APPEND MELON_LINK_LIBRARIES unofficial-sodium::sodium)
  endif()
endif()

list(APPEND MELON_LINK_LIBRARIES ${CMAKE_DL_LIBS})
list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_DL_LIBS})

if (PYTHON_EXTENSIONS)
  find_package(PythonInterp 3.6 REQUIRED)
  find_package(Cython 0.26 REQUIRED)
endif ()

find_package(LibUnwind)
if (LIBUNWIND_FOUND)
  set(MELON_HAVE_LIBUNWIND ON)
  list(APPEND MELON_LINK_LIBRARIES ${LIBUNWIND_LIBRARIES})
  list(APPEND MELON_INCLUDE_DIRECTORIES ${LIBUNWIND_INCLUDE_DIRS})
endif()
if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
  list(APPEND MELON_LINK_LIBRARIES "execinfo")
endif ()

cmake_push_check_state()
set(CMAKE_REQUIRED_DEFINITIONS -D_XOPEN_SOURCE)
check_cxx_symbol_exists(swapcontext ucontext.h MELON_HAVE_SWAPCONTEXT)
cmake_pop_check_state()

set(MELON_USE_SYMBOLIZER OFF)
CHECK_INCLUDE_FILE_CXX(elf.h MELON_HAVE_ELF)
find_package(Backtrace)

set(MELON_HAVE_BACKTRACE ${Backtrace_FOUND})
set(MELON_HAVE_DWARF ${LIBDWARF_FOUND})
if (NOT WIN32 AND NOT APPLE)
  set(MELON_USE_SYMBOLIZER ON)
endif()
message(STATUS "Setting MELON_USE_SYMBOLIZER: ${MELON_USE_SYMBOLIZER}")
message(STATUS "Setting MELON_HAVE_ELF: ${MELON_HAVE_ELF}")
message(STATUS "Setting MELON_HAVE_DWARF: ${MELON_HAVE_DWARF}")

# Using clang with libstdc++ requires explicitly linking against libatomic
check_cxx_source_compiles("
  #include <atomic>
  int main(int argc, char** argv) {
    struct Test { bool val; };
    std::atomic<Test> s;
    return static_cast<int>(s.is_lock_free());
  }"
  MELON_CPP_ATOMIC_BUILTIN
)
if(NOT MELON_CPP_ATOMIC_BUILTIN)
  list(APPEND CMAKE_REQUIRED_LIBRARIES atomic)
  list(APPEND MELON_LINK_LIBRARIES atomic)
  check_cxx_source_compiles("
    #include <atomic>
    int main(int argc, char** argv) {
      struct Test { bool val; };
      std::atomic<Test> s2;
      return static_cast<int>(s2.is_lock_free());
    }"
    MELON_CPP_ATOMIC_WITH_LIBATOMIC
  )
  if (NOT MELON_CPP_ATOMIC_WITH_LIBATOMIC)
    message(
      FATAL_ERROR "unable to link C++ std::atomic code: you may need \
      to install GNU libatomic"
    )
  endif()
endif()

check_cxx_source_compiles("
  #include <type_traits>
  #if _GLIBCXX_RELEASE
  int main() {}
  #endif"
  MELON_STDLIB_LIBSTDCXX
)
check_cxx_source_compiles("
  #include <type_traits>
  #if _GLIBCXX_RELEASE >= 9
  int main() {}
  #endif"
  MELON_STDLIB_LIBSTDCXX_GE_9
)
check_cxx_source_compiles("
  #include <type_traits>
  #if _LIBCPP_VERSION
  int main() {}
  #endif"
  MELON_STDLIB_LIBCXX
)
check_cxx_source_compiles("
  #include <type_traits>
  #if _LIBCPP_VERSION >= 9000
  int main() {}
  #endif"
  MELON_STDLIB_LIBCXX_GE_9
)
check_cxx_source_compiles("
  #include <type_traits>
  #if _CPPLIB_VER
  int main() {}
  #endif"
  MELON_STDLIB_LIBCPP
)
find_package(ksimd REQUIRED)
include(check_target_intrinsic)
message(STATUS "KSIMD_ARCH_OPTION: ${KSIMD_ARCH_OPTION}")
if (APPLE)
  list (APPEND CMAKE_REQUIRED_LIBRARIES c++abi)
  list (APPEND MELON_LINK_LIBRARIES c++abi)
endif ()

if (MELON_STDLIB_LIBSTDCXX AND NOT MELON_STDLIB_LIBSTDCXX_GE_9)
  list (APPEND CMAKE_REQUIRED_LIBRARIES stdc++fs)
  list (APPEND MELON_LINK_LIBRARIES stdc++fs)
endif()
if (MELON_STDLIB_LIBCXX AND NOT MELON_STDLIB_LIBCXX_GE_9)
  list (APPEND CMAKE_REQUIRED_LIBRARIES c++fs)
  list (APPEND MELON_LINK_LIBRARIES c++fs)
endif ()

option(
  MELON_LIBRARY_SANITIZE_ADDRESS
  "Build melon with Address Sanitizer enabled."
  OFF
)

if ($ENV{WITH_ASAN})
  message(STATUS "ENV WITH_ASAN is set")
  set (MELON_LIBRARY_SANITIZE_ADDRESS ON)
endif()

if (MELON_LIBRARY_SANITIZE_ADDRESS)
  if ("${CMAKE_CXX_COMPILER_ID}" MATCHES GNU)
    set(MELON_LIBRARY_SANITIZE_ADDRESS ON)
    set(MELON_ASAN_FLAGS -fsanitize=address,undefined)
    list(APPEND MELON_CXX_FLAGS ${MELON_ASAN_FLAGS})
    # All of the functions in melon/detail/Sse.cpp are intended to be compiled
    # with ASAN disabled.  They are marked with attributes to disable the
    # sanitizer, but even so, gcc fails to compile them for some reason when
    # sanitization is enabled on the compile line.
    set_source_files_properties(
      "${PROJECT_SOURCE_DIR}/melon/detail/sse.cpp"
      PROPERTIES COMPILE_FLAGS -fno-sanitize=address,undefined
    )
  elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES Clang)
    set(MELON_LIBRARY_SANITIZE_ADDRESS ON)
    set(
      MELON_ASAN_FLAGS
      -fno-common
      -fsanitize=address,undefined,integer,nullability
      -fno-sanitize=unsigned-integer-overflow
    )
    list(APPEND MELON_CXX_FLAGS ${MELON_ASAN_FLAGS})
  endif()
endif()
#[[
add_library(melon_deps INTERFACE)

find_package(fmt CONFIG REQUIRED)
target_link_libraries(melon_deps INTERFACE fmt::fmt)

list(REMOVE_DUPLICATES MELON_INCLUDE_DIRECTORIES)
target_include_directories(melon_deps INTERFACE ${MELON_INCLUDE_DIRECTORIES})
target_link_libraries(melon_deps INTERFACE
  ${MELON_LINK_LIBRARIES}
  ${MELON_SHINY_DEPENDENCIES}
  ${MELON_ASAN_FLAGS}
)
]]
find_package(fmt REQUIRED)
get_target_property(INCLUDE_DIRS Boost::headers INTERFACE_INCLUDE_DIRECTORIES)
include_directories(${INCLUDE_DIRS})
set(DEPS_LIB
        fmt::fmt
        ${MELON_LINK_LIBRARIES}
        ${MELON_SHINY_DEPENDENCIES}
        ${MELON_ASAN_FLAGS}
        )